package com;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.xml.parsers.ParserConfigurationException;

import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.SimpleWeightedGraph;

import netscape.javascript.JSObject;

@SuppressWarnings("serial")
public class AlibiTesterApplet extends JApplet implements ActionListener {
	
	/*This class extends JApplet and it is responsible for implementing the applet. */
	
	//This will hold the graph of Cambridge parsed from the XML file.
	public static SimpleWeightedGraph<Node, DefaultWeightedEdge> oldgraph;
	
	//An ArrayList to hold the Markers.
	static ArrayList<Marker> markers = new ArrayList<Marker>();
	
	//An ArrayList to hold the waypoints to show the route in the Map.
	static ArrayList<String> waypoints = new ArrayList<String>();
	
	//Define components globally so actionPerformed can see them.
	JComboBox speedList;
	JButton buttnBasic, buttnAdv, bttnCloseAdv;
	JFrame frameAdv;
	
	//The type of transport chosen in the GUI.
	public static String type;
	
	public void init() {
	//Where all the initialisation happens.
		//Create a JPanel.
		JPanel contentPane = new JPanel(new GridLayout(4,1));
		
		//Create and add text label.
		JLabel label = new JLabel("Choose a mode of transport:");
		label.setHorizontalAlignment(JLabel.CENTER);
		contentPane.add(label);
		
		//Create and add the ComboBox.
		String[] speeds = { "Walking", "Running", "Driving"};
        speedList = new JComboBox(speeds);
        contentPane.add(speedList);
        
        //Create and add the first button.
        buttnBasic = new JButton("Calculate min. time");
        buttnBasic.addActionListener(this);
        contentPane.add(buttnBasic);
        
        //Create and add the other button.
        buttnAdv = new JButton("Advanced...");
        buttnAdv.addActionListener(this);
        contentPane.add(buttnAdv);
        
        //Set the applet to display the panel.
        this.setContentPane(contentPane);
        
        //Creates a graph containing the Cam data parsed from the XML file.
        //Do this here so it only executes once and doesnt slow down getting the result.
        try {oldgraph = ParseAndCreateGraph.getGraph();} 
        catch (ParserConfigurationException e) {e.printStackTrace();}
		 
	}
 
	public void actionPerformed(ActionEvent evt) {
		if (evt.getSource() == buttnBasic) {
		//When the main button is clicked, show a dialog box with the answer.
			//run TS and get answer.
			int time = callTS();
			//if there are less than 2 markers just return.
			if (time==0) {return;} else {}
			//Message to be shown in title bar of dialog box.
			String title = "Alibi Tester";
			//Message to be shown in body of dialog box.
			String message = "Minimum time for route: " + time + " minutes.";
			
			//Convert ArrayList into Array to serve as JS argument.
			String[] wps = waypoints.toArray(new String[0]);
			//Show the route in the Map by calling the JS function.
			((JSObject) JSObject.getWindow(this)).call("showRoute", new Object[] {wps});
			//Reset the Array List of waypoints afterwards.
			waypoints.clear();
			
			//Create the dialog box.
			JOptionPane.showMessageDialog(null, message, title, JOptionPane.INFORMATION_MESSAGE);
			//clear the list of markers.
			markers.clear();
			//clear the list of new nodes.
			TopLevel.newNodes.clear();
			//call the JS function to clear and reset the map.
			((JSObject) JSObject.getWindow(this)).eval("reset()");
		}
		if (evt.getSource() == buttnAdv) {
		//When the button is clicked, open a new window with the results.		
			//Create a new frame.
			frameAdv = new JFrame("Algorithm Comparison");
			frameAdv.setLayout(new BorderLayout());
			
			//Create a grid panel.
			JPanel tsData = new JPanel(new GridLayout(5,1));
			
			//Run TS greedy.
			int result1 = callTS();
			//If there are less than 2 markers just return.
			if (result1==0) {return;} else {}
			//Compute execution time.
			String time = "Exectution time: " + (TopLevel.endTime - TopLevel.startTime)/1000;
			//Create and add a label with the answer and execution time.
			JLabel greedyRes = new JLabel("Greedy algorithm: " + result1 + " minutes. " + time);
			greedyRes.setHorizontalAlignment(JLabel.CENTER);
			tsData.add(greedyRes);
			
			//Run TS random.
			long startTime = System.nanoTime();
			double min_dist = TSRandom.compute(TopLevel.locationsGraph);
			long endTime = System.nanoTime();
			double min_timeDouble = ReturnTimeFromType.getTime(min_dist);
			int result2 = (int) Math.rint(min_timeDouble);
			//Compute execution time.
			time = "Exectution time: " + (endTime - startTime)/1000;
			//Create and add a label with the answer and execution time.
			JLabel ranRes = new JLabel("Random algorithm: " + result2 + " minutes. " + time);
			ranRes.setHorizontalAlignment(JLabel.CENTER);
			tsData.add(ranRes);
			
			//Run TS random insertion.
			startTime = System.nanoTime();
			min_dist = TSRanInsertion.compute(TopLevel.locationsGraph);
			endTime = System.nanoTime();
			min_timeDouble = ReturnTimeFromType.getTime(min_dist);
			int result3 = (int) Math.rint(min_timeDouble);
			//Compute execution time.
			time = "Exectution time: " + (endTime - startTime)/1000;
			//Create and add a label with the answer and execution time.
			JLabel ranInsRes = new JLabel("Random Insertion algorithm: " + result3 + " minutes. " + time);
			ranInsRes.setHorizontalAlignment(JLabel.CENTER);
			tsData.add(ranInsRes);
			
			//Run TS cheap insertion.
			startTime = System.nanoTime();
			min_dist = TSCheapIns.compute(TopLevel.locationsGraph);
			endTime = System.nanoTime();
			min_timeDouble = ReturnTimeFromType.getTime(min_dist);
			int result4 = (int) Math.rint(min_timeDouble);
			//Compute execution time.
			time = "Exectution time: " + (endTime - startTime)/1000;
			//Create and add a label with the answer and execution time.
			JLabel cheInsRes = new JLabel("Cheap Insertion algorithm: " + result4 + " minutes. " + time);
			cheInsRes.setHorizontalAlignment(JLabel.CENTER);
			tsData.add(cheInsRes);
			
			//Run TS pairwise exchange.
			startTime = System.nanoTime();
			min_dist = TSPairwiseEx.compute(TopLevel.locationsGraph);
			endTime = System.nanoTime();
			min_timeDouble = ReturnTimeFromType.getTime(min_dist);
			int result5 = (int) Math.rint(min_timeDouble);
			//Compute execution time.
			time = "Exectution time: " + (endTime - startTime)/1000;
			//Create and add a label with the answer and execution time.
			JLabel pWRes = new JLabel("Pairwise Exhange algorithm: " + result5 + " minutes. " + time);
			pWRes.setHorizontalAlignment(JLabel.CENTER);
			tsData.add(pWRes);
			
			//Add the grid panel to the frame.
			frameAdv.add(tsData, BorderLayout.CENTER);
			
			//Create and add the close button to the frame.
			bttnCloseAdv = new JButton("Close");
			bttnCloseAdv.addActionListener(this);
			frameAdv.add(bttnCloseAdv, BorderLayout.PAGE_END);
			
			//Place window in the centre of the screen.
			frameAdv.setLocationRelativeTo(null);
			//Make the frame have the correct size.
			frameAdv.pack();
			//Display the frame.
			frameAdv.setVisible(true);
		}
		if (evt.getSource() == bttnCloseAdv) {
		//When button is clicked, close the window.
			//clear the list of markers.
			markers.clear();
			//clear the list of new nodes.
			TopLevel.newNodes.clear();
			//call the JS function to clear and reset the map.
			((JSObject) JSObject.getWindow(this)).eval("reset()");
			//close window.
			frameAdv.dispose();
		}
		
	}

	public void AddMarkerToArray(String lat, String lon) {
	//Take the lat and lon values of map locations from JS and convert them into Markers. 
		Marker mrk = new Marker(lat, lon);
		markers.add(mrk);
	}
	
	public int callTS() {
		//initialise variable that will hold the answer.
		int time = 0;
		//Get the type chosen by the user in the ComboBox.
		type = (String)speedList.getSelectedItem();
		//If there are less than 2 markers alert the user and return. 
		if (markers.isEmpty() || markers.size() == 1) {
			//clear the list of markers.
			markers.clear();
			//Alert user with a pop-up.
			String message2 = "Add at least 2 markers on the map and then try again.";
			JOptionPane.showMessageDialog(null, message2, "Alibi Tester", JOptionPane.INFORMATION_MESSAGE);
			//call the JS function to clear and reset the map.
			((JSObject) JSObject.getWindow(this)).eval("reset()");
			//Return 'time' equals zero.
			return time;
		}
		//Get minimum time and return it.
		time = TopLevel.doTheWork();
		
		return time;
	}

}
